#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

using namespace std;

#define BUF_SZ 128

#define SAT_IPv4 1
#define SAT_IPv6 4
#define SAT_DOMAIN 3

//write all data
int safeWrite(const int fd,const void *buf, size_t count)
{  
  char *b = (char*) buf;
  while(count)
  {
    int c = write(fd, b, count);
    if(c<0)
    {
      perror("Chyba pri write");
      exit(1);            
    }
    b+=c;
    count-=c;
  }
  return count;
}

//read n bytes
uint8_t * safeRead(const int fd, size_t count)
{ 
  uint8_t * res = new uint8_t[count];
  uint req_index = 0;
  uint8_t buf[count];
  int cnt = -1; 
  while((cnt = read(fd,buf,count))>0)
  {    
    for(int i = 0;i<cnt;i++)
    {
      res[req_index++]=buf[i];
    }           
    if(req_index==count) break;
  }
  
  //FIXME throw error if cnt<0
 
  return res;
}

void copy(const int src, const int dest)
{  
  string request = "";
  char buf[BUF_SZ];
  int cnt = -1;
        
  while((cnt = read(src,buf,sizeof(buf)))>0)
  {   
    safeWrite(dest, buf, cnt);
  }
  close(src);  
}


string getAddrFromDomainName(char * dname)
{
	struct addrinfo *adresa;

	if(getaddrinfo(dname, NULL, NULL, &adresa) != 0)
	{
		perror("getaddrinfo");
	}

	string retval(inet_ntoa((*(struct sockaddr_in *)(adresa->ai_addr)).sin_addr));
	printf("Server Address:  %s\n", retval.c_str());

	// ukoncenie prace s addrinfo
	freeaddrinfo(adresa);
	return retval;
}

void exchangeData(int sock1, int sock2, bool secondIsUDP = false)
{
    fd_set socks;    
    FD_ZERO(&socks);    
    FD_SET(sock1,&socks);
    FD_SET(sock2,&socks);
    int n = 0;
    
    bool closed1 = 0, closed2 = 0;
    
    while((n = select(max(sock1,sock2)+1,&socks,NULL,NULL,NULL)))
    {      
      if(FD_ISSET(sock2,&socks))
      {        
        int cnt=0;
        const int tmpBufferSize = (secondIsUDP)?65535:BUF_SZ; // maximal UDP payload size
        char buf[tmpBufferSize];
        if((cnt = read(sock2,buf,tmpBufferSize))>=0)
        {
          safeWrite(sock1,buf,cnt);
        }       
        if(cnt==0 && !closed1)
        {
          cout << "Closing outsock read" << endl;                 
          shutdown(sock2, SHUT_RD);             
          shutdown(sock1, SHUT_WR);          
          closed2 = 1;
        }
        
      }    
      
      if(FD_ISSET(sock1,&socks))
      {              
        int cnt=0;
        char buf[BUF_SZ];
        if((cnt = read(sock1,buf,BUF_SZ))>=0)
        {
          safeWrite(sock2,buf,cnt);
        }
        if(cnt==0 && !closed2)
        {
          cout << "Closing clientsock read" << endl;       
          shutdown(sock1, SHUT_RD);          
          shutdown(sock2, SHUT_WR);          
          closed1 = 1;
        }        
      }
      
      if(closed1 && closed2)
      {      
        cout << "Closing" << endl;
        close(sock1);
        close(sock2);
        exit(0);
      }
      
      //reset FD SET      
      FD_ZERO(&socks);    
      if(!closed1)
        FD_SET(sock1,&socks);
      if(!closed2)
        FD_SET(sock2,&socks);
    }  
}

void childdied(int sig)
{
  while(waitpid(-1,NULL,WNOHANG)>0);
  signal(SIGCHLD,childdied);
}

string extractFname(char* file)
{
  string s = file;
  size_t pos = string::npos;
  if((pos = s.find_last_of('/')) != string::npos)
  {
     return s.substr(pos+1);
  }
  return s;
}

void stop(string fname)
{
  //send quit signal
  system(("killall -s 3 "+fname).c_str());
}
